package com.mawell.common.propertymapperswing;

import org.apache.log4j.Logger;

import javax.swing.JTable;
import javax.swing.table.*;

import java.util.*;

/**
 * JTable that maps a Map object to a name-value table.
 * Allows for simple editing of properties.
 *
 * @author David Ekholm
 * @see java.util.Map
 */
public class JMapTable extends JTable {

    private static Logger logger = Logger.getLogger(JMapTable.class);

    private boolean editable = true;

    public void setCellEditable(boolean editable) {
        this.editable = editable;
    }

    class MapModel extends AbstractTableModel {

        public String[] colNames = {
                "Name", "Value"};

        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return editable;
        }

        public String getColumnName(int col) {
            return colNames[col];
        }

        public int getColumnCount() {
            return 2;
        }

        public int getRowCount() {
            return items.size() + 1; // One empty last row
        }

        public Object getValueAt(int row, int col) {
            if(row >= items.size()) {
                return "";
            }
            Map.Entry item = (Map.Entry)items.get(row);
            return (col == 0) ? item.getKey() : item.getValue();
        }

        public void setValueAt(Object aValue, int row, int col) {
            if(row >= items.size()) { // Sometimes we can really get larger row than items.size()
                Entry item;
                if(col == 0) {
                    item = new Entry(aValue, "");
                } else {
                    item = new Entry("", aValue);
                }
                items.add(item);
            } else {
                Map.Entry oldItem = (Map.Entry)items.get(row);
                Entry item;
                if(col == 0) {
                    item = new Entry(aValue, oldItem.getValue());
                } else {
                    item = new Entry(oldItem.getKey(), aValue);
                }
                items.set(row, item);
            }
            fireTableDataChanged();
        }
    }

    private Vector items = new Vector();
    private MapModel theModel = new MapModel();

    public JMapTable() {
        super.setModel(theModel);
        super.setRowSelectionAllowed(false);
    }

    public JMapTable(String nameLabel, String valueLabel) {
        setHeaderNames(nameLabel, valueLabel);
        super.setModel(theModel);
        super.setRowSelectionAllowed(false);
    }

    public void setHeaderNames(String nameLabel, String valueLabel) {
        theModel = new MapModel();
        theModel.colNames[0] = nameLabel;
        theModel.colNames[1] = valueLabel;
        super.setModel(theModel);
    }

    public void clear() {
        items.clear();
    }

    public Map getMap() {
        TableCellEditor editor = getCellEditor();
        if(editor != null) {
            editor.stopCellEditing(); // An editing is being performed. Notify model

        }
        HashMap theMap = new HashMap();
        for(int i = 0; i < items.size(); i++) {
            Map.Entry item = (Map.Entry)items.get(i);
            Object key, value;
            if(item.getKey() instanceof IDItem) {
                key = ((IDItem)item.getKey()).id;
            } else {
                key = item.getKey();
            }

            if(item.getValue() instanceof IDItem) {
                value = ((IDItem)item.getValue()).id;
            } else {
                value = item.getValue();
            }

            if((key != null && !key.toString().equals(""))
                    || (value != null && !value.toString().equals(""))) {
                theMap.put(key, value);
            }
        }

        return theMap;
    }

    public void putAll(Map theMap) {
        Iterator it = theMap.entrySet().iterator();
        while(it.hasNext()) {
            items.add(it.next());
        }
    }

    public void put(Object key, Object value) {
        items.add(new Entry(key, value));
    }

    class Entry implements Map.Entry {
        Object key;
        Object value;

        public Entry(Object key, Object value) {
            this.key = key;
            this.value = value;
        }

        public boolean equals(Object other) {
            return key.equals(((Entry)other).key);
        }

        public Object getKey() {
            return key;
        }

        public Object getValue() {
            return value;
        }

        public Object setValue(Object parm1) {
            value = parm1;
            return this;
        }
    }
}